package net.pulseproject.router.modules.logic.valuethreshold;

import java.util.Timer;
import java.util.TimerTask;

import javax.sound.midi.ShortMessage;

import net.pulseproject.commons.midi.entity.MidiMessageStatus;
import net.pulseproject.commons.util.MathUtil;
import net.pulseproject.commons.util.MidiUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class MultipleMessageSender {
	
	private static final Log LOG = LogFactory.getLog(MultipleMessageSender.class);
	
	private static final int DELAY_BETWEEN_MESSAGES_IN_MS = 20;
	
	private final MessageBroadcaster broadcaster;
	
	private final int startValue;
	
	private final int endValue;
	
	private final Timer timer = new Timer();

	private final TimerTask task;
	
	private boolean running = false;
	
	private boolean wasStopped = false;

	private final int maxCount;
	
	private int count = 0;
	
	public MultipleMessageSender(
			final MessageBroadcaster broadcaster,
			final int startValue,
			final int endValue,
			final int transformationTimeInMs) {
		this.broadcaster = broadcaster;
		this.startValue = startValue;
		this.endValue = endValue;
		
		this.maxCount = transformationTimeInMs / DELAY_BETWEEN_MESSAGES_IN_MS;
		
		this.task = new TimerTask() {
			@Override public void run() {
				MultipleMessageSender.this.onTimer();
			}
		};
	}

	public final void start() {
		LOG.info("start()");
		
		if(this.wasStopped == true || this.isRunning() == true) {
			throw new IllegalStateException("Can not reuse sender! (wasStopped=" + this.wasStopped + ", " +
					"isRunning=" + this.isRunning() + ")");
		}
		
		this.running = true;
		
		this.timer.schedule(this.task, 0, DELAY_BETWEEN_MESSAGES_IN_MS);
	}
	
	private void onTimer() {
		try {
			LOG.trace("onTimer() ... count " + this.count + " / " + this.maxCount);
			
			if(this.count > this.maxCount) {
				LOG.debug("maxCount of [" + this.maxCount + "] reached, aborting.");
				this.stop();
				return;
			}
			
			final float zeroBasedPercent = (this.count / ((float) this.maxCount));
			
			final int status = MidiMessageStatus.CONTROLLER_CHANNEL_1.getValue(); // will be overwritten by rerouter
			final int data1 = 42; // control number will be overwritten by rerouter
			final int data2 = MathUtil.computeCubicEaseOut(zeroBasedPercent, this.startValue, this.endValue);
			
			final ShortMessage message = MidiUtil.createMessageByStatus(status, data1, data2);
			
			this.broadcaster.broadcast(message);
			this.count++;
			
		} catch(final Exception e) {
			LOG.error("Sending MIDI message failed!", e);
			this.stop();
		}
	}
	
	public final void stop() {
		LOG.info("stop()");
		
		if(this.wasStopped == true || this.isRunning() == false) {
			throw new IllegalStateException("Can not stop sender! (wasStopped=" + this.wasStopped + ", " +
					"isRunning=" + this.isRunning() + ")");
		}
		
		this.wasStopped = true;
		this.running = false;
		
		this.timer.cancel();
	}
	
	public final boolean isRunning() {
		return this.running;
	}

}
